#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
// #include <linux/ide.h>
#include <linux/fs.h>

#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
// #include <asm/math/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

/*
描述：
版本：
作者：

*/

#define LED_COUNT 1       // 主设备号
#define LED_NAME "newled" // 设备名称

#define ON 1  // 亮灯
#define OFF 0 // 灭灯

// 寄存器物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)

// 映射后寄存器虚拟地址
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

struct newled_dev
{
    dev_t devid;         // 设备号
    struct cdev cdev;    // cdev
    struct class *class; //
    struct device *device;
    int major;
    int minor;
};

// 开关灯
void led_switch(u8 state)
{
    u32 val = 0;
    if (state == ON)
    {
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }
    else if (state == ON)
    {
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        writel(val, GPIO1_DR);
    }
    else
    {
    }
}

struct newled_dev newled;

// 定义file_operations

static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &newled;
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue = 0;
    unsigned char databuf[1] = {0};
    unsigned char ledstate = 0;
    retvalue = copy_from_user(databuf, buf, cnt);
    if (retvalue < 0)
    {
        printk("kernel write failed!\r\n");
    }
    ledstate = databuf[0];
    if (ledstate == ON)
    {
        led_switch(ON);
    }
    else if (ledstate == OFF)
    {
        led_switch(OFF);
    }
    else
    {
    }

    return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations led_fops =
    {
        .owner = THIS_MODULE,
        .open = led_open,
        .read = led_read,
        .write = led_write,
        .release = led_release,
};

static int __init led_init(void)
{
    u32 val = 0;
    /* 初始化 LED */
    /* 1、寄存器地址映射 */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    /* 2、使能 GPIO1 时钟 */
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3 << 26); /* 清楚以前的设置 */
    val |= (3 << 26);  /* 设置新值 */
    writel(val, IMX6U_CCM_CCGR1);

    writel(5, SW_MUX_GPIO1_IO03);
    /* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */
    writel(0x10B0, SW_PAD_GPIO1_IO03);
    /* 4、设置 GPIO1_IO03 为输出功能 */
    val = readl(GPIO1_GDIR);
    val &= ~(1 << 3); /* 清除以前的设置 */
    val |= (1 << 3);  /* 设置为输出 */
    writel(val, GPIO1_GDIR);

    /* 5、默认关闭 LED */
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);

    //注册设备
    //申请设备号

    if(newled.major)
    {
        newled.devid=MKDEV(newled.major,0);
        register_chrdev_region(newled.devid,LED_COUNT,LED_NAME);
    }
    else
    {
        alloc_chrdev_region(&newled.devid,0,LED_COUNT,LED_NAME);
        newled.major=MAJOR(newled.devid);
        newled.minor=MINOR(newled.devid);
    }

    //初始化
    newled.cdev.owner=THIS_MODULE;
    cdev_init(&newled.cdev,&led_fops);


    //添加
    cdev_add(&newled.cdev,newled.devid,LED_COUNT);

    //创建类
    newled.class=class_create(THIS_MODULE,LED_NAME);

    if(IS_ERR(newled.class))
    {
        return PTR_ERR(newled.class);
    }

    //创建设备节点

    newled.device=device_create(newled.class,NULL,newled.devid,NULL,LED_NAME);

    if(IS_ERR(newled.device))
    {
        return PTR_ERR(newled.device);
    }

    return 0;
}

static void __exit led_exit(void)
{
    /* 取消映射 */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    // 注销字符设备
    cdev_del(&newled.cdev);
    unregister_chrdev_region(newled.devid,LED_COUNT);


    device_destroy(newled.class,newled.devid);
    class_destroy(newled.class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("spf");